自定义View(4) -- 字体变色

字体变色.gif

初始化我就不说了,先思考我们需要什么属性,这里我就随便写了两个,一个是变色的颜色,一个是正常的颜色,当然也可以是默认的字体颜色,我们在attr里面申明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<declare-styleable name="ColorTrackTextView">
<attr name="originColor" format="color" />
<attr name="changeColor" format="color" />
</declare-styleable>

private int mOriginColor;//不变化的颜色
private int mChangeColor;//变化的颜色
private Paint mOriginPaint, mChangePaint;

private void init(Context context, AttributeSet attrs) {
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ColorTrackTextView);
mOriginColor = ta.getColor(R.styleable.ColorTrackTextView_originColor, Color.BLACK);
mChangeColor = ta.getColor(R.styleable.ColorTrackTextView_changeColor, Color.RED);
ta.recycle();

mOriginPaint = new Paint();
mOriginPaint.setColor(mOriginColor);
mOriginPaint.setAntiAlias(true);
mOriginPaint.setTextSize(getTextSize());

mChangePaint = new Paint();
mChangePaint.setColor(mChangeColor);
mChangePaint.setAntiAlias(true);
mChangePaint.setTextSize(getTextSize());

}

因为我们选择了继承的是TextView,所以我们就不进行 onMeasure(),我们重写onDraw(),覆盖原有的绘制逻辑,我们自己来绘制,这里我们主要使用canvas.clipRect(rect)这个函数来实现裁剪,我们先来测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//思路:利用clipRect  来裁剪   使用两个画笔
@Override
protected void onDraw(Canvas canvas) {
// super.onDraw(canvas); //不使用TextView的绘制 自己画

canvas.save();
int mid = 500;
Rect rect = new Rect(0, 0, mid, getHeight());
canvas.clipRect(rect);

String text = getText().toString();
int x = (int) (getPaddingLeft() + getWidth() / 2 - mOriginPaint.measureText(text) / 2);
int y = getPaddingTop() + DisplayUtil.getTextBaseLine(getHeight(), mOriginPaint);
canvas.drawText(text, x, y, mChangePaint);
canvas.restore();

canvas.save();
rect.set(mid, 0, getWidth(), getHeight());
canvas.clipRect(rect);
canvas.drawText(text, x, y, mOriginPaint);
canvas.restore();
}

<com.zzw.customview.view.ColorTrackTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/colortv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
android:gravity="center"
android:text="1111111"
android:textSize="50sp"
app:changeColor="@color/colorAccent"
app:originColor="@color/colorPrimary" />

可以看到,我们是有效果的,因为我们考略到要和 ViewPager 一起配合使用,所以我们定义一个float类型的mCurrentProgress属性,用于表示当前的滑动进度,在设置一个方向,颜色是从左到右变化还是从右到左的变化

1
2
3
4
5
6
7
8
9
//不同的朝向
public static final int DIRECTION_LEFT_TO_RIGHT = 1;//从左边变色
public static final int DIRECTION_RIGHT_TO_LEFT = 2;//从右边变色


private int mDirection = DIRECTION_LEFT_TO_RIGHT;

//当前进度
private float mCurrentProgress;

接下来我们修改onDraw()以及优化一下赘余代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
 //思路:利用clipRect  来裁剪   使用两个画笔
@Override
protected void onDraw(Canvas canvas) {
// super.onDraw(canvas); //不使用TextView的绘制 自己画

int mid = comMiddle();
if (mDirection == DIRECTION_LEFT_TO_RIGHT) {
drawText(canvas, mChangePaint, 0, mid); //画左边 颜色
drawText(canvas, mOriginPaint, mid, getWidth());//画右边
} else {
mid = getWidth() - mid;
drawText(canvas, mChangePaint, mid, getWidth());//画右边 颜色
drawText(canvas, mOriginPaint, 0, mid); //画左边
}
}

/**
* 根据当前进度算出中间值
*
* @return
*/
private int comMiddle() {
return (int) (mCurrentProgress * getWidth());
}


/**
* 根据start end 确定rect绘制文字
*
* @param canvas
* @param paint
* @param start
* @param end
*/
private void drawText(Canvas canvas, Paint paint, int start, int end) {
canvas.save();
Rect rect = new Rect(start, 0, end, getHeight());//确定区域
canvas.clipRect(rect);

String text = getText().toString();
int x = (int) (getPaddingLeft() + getWidth() / 2 - paint.measureText(text) / 2);
int y = getPaddingTop() + DisplayUtil.getTextBaseLine(getHeight(), paint);
canvas.drawText(text, x, y, paint);
canvas.restore();
}

接下来我们进行测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public void leftToRight(View view) {
mColorTrackTextView.setDirection(ColorTrackTextView.DIRECTION_LEFT_TO_RIGHT);
startAnim(0, 1);
}

public void RightToLeft(View view) {
mColorTrackTextView.setDirection(ColorTrackTextView.DIRECTION_RIGHT_TO_LEFT);
startAnim(0, 1);
}


private void startAnim(float startPro, float endPro) {
ValueAnimator animator = ObjectAnimator.ofFloat(startPro, endPro);
animator.setDuration(2000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float currentProgress = (float) animation.getAnimatedValue();
mColorTrackTextView.setCurrentProgress(currentProgress);
}
});
animator.start();
}

就会得到如下图的效果:
字体变色测试.gif

感觉离成功近了一步,这里我们顺便优化了一个性能的问题,因为是不端的调用setCurrentProgress()方法进行重新绘制,所以这里我们加个判断:

1
2
3
4
5
6
7
public void setCurrentProgress(float currentProgress) {
if (mCurrentProgress == currentProgress)//当前进度相同就不执行下一步
return;

this.mCurrentProgress = currentProgress;
invalidate();
}

让它重复的时候不进行重绘,这只是一个小细节,接下来我们和ViewPager配合使用。我们在开发中一般遇到的是根据一个数据源,然后动态的添加FragmentViewPager进行关联,这里我们模拟这个场景。我们使用一个LinearLayout来管理这些字体变色的viewViewPagerOnPageChangeListener来管理字体变色,布局为下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">

<LinearLayout
android:layout_width="match_parent"
android:orientation="horizontal"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:id="@+id/indicator_view"
android:layout_height="wrap_content"/>

<android.support.v4.view.ViewPager
android:layout_width="match_parent"
android:layout_height="0dp"
android:id="@+id/view_pager"
android:layout_weight="1"
/>
</LinearLayout>

然后我们在代码中动态的添加:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package com.zzw.customview;

import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;

import com.zzw.customview.view.ColorTrackTextView;

import java.util.ArrayList;
import java.util.List;

/**
* Created by zzw on 2017/6/15.
* Version:
* Des: 字体变色和viewpager配合使用
*/
public class ViewPagerActivity extends AppCompatActivity {
private String[] items = {"热点", "推荐", "社会", "图片", "科技", "运动"};
private LinearLayout mIndicatorContainer;// 变成通用的
private List<ColorTrackTextView> mIndicators;
private ViewPager mViewPager;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_pager);

mIndicators = new ArrayList<>();
mIndicatorContainer = (LinearLayout) findViewById(R.id.indicator_view);
mViewPager = (ViewPager) findViewById(R.id.view_pager);
initIndicator();
initViewPager();
}

/**
* 初始化ViewPager
*/
private void initViewPager() {
mViewPager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {
@Override
public Fragment getItem(int position) {
return ItemFragment.newInstance(items[position]);
}

@Override
public int getCount() {
return items.length;
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {

}
});

mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
Log.e("TAG", "position -> " + position + " positionOffset -> " + positionOffset + " positionOffsetPixels->" + positionOffsetPixels);
// position 代表当前的位置
// positionOffset 代表滚动的 0 - 1 百分比 左滑 1->0 右滑-> 0-1

// 1.左边 位置 position
ColorTrackTextView left = mIndicators.get(position);
left.setDirection(ColorTrackTextView.DIRECTION_RIGHT_TO_LEFT);
left.setCurrentProgress(1 - positionOffset);

try {
ColorTrackTextView right = mIndicators.get(position + 1);
right.setDirection(ColorTrackTextView.DIRECTION_LEFT_TO_RIGHT);
right.setCurrentProgress(positionOffset);
} catch (Exception e) {

}
}

@Override
public void onPageSelected(int position) {

}

@Override
public void onPageScrollStateChanged(int state) {

}
});
}

/**
* 初始化可变色的指示器
*/
private void initIndicator() {
for (int i = 0; i < items.length; i++) {
// 动态添加颜色跟踪的TextView
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
params.weight = 1;
ColorTrackTextView colorTrackTextView = new ColorTrackTextView(this);
// 设置颜色
colorTrackTextView.setTextSize(20);
colorTrackTextView.setChangeColor(Color.RED);
colorTrackTextView.setText(items[i]);
colorTrackTextView.setLayoutParams(params);

// 把新的加入LinearLayout容器
mIndicatorContainer.addView(colorTrackTextView);
// 加入集合
mIndicators.add(colorTrackTextView);
}
}

这里我们发现左右滑动的时候可以实现了,但是点击tab的时候还会出现上一个没有变色的情况,这里我就不上图了,我们直接写一个函数,在选中之后把重新设置颜色即可。

1
2
3
4
5
6
7
8
9
10
private void selectPos(int pos) {
for (int i = 0; i < mIndicators.size(); i++) {
ColorTrackTextView colorTrackTextView = mIndicators.get(i);
if (i == pos) {
colorTrackTextView.setCurrentProgress(1.0f);
} else {
colorTrackTextView.setCurrentProgress(0.0f);
}
}
}

最后要说的是,优化问题,在这篇文章中我们在绘制的过程中就已经进行优化了,我们要养成这种好习惯,不管是代码上还是性能上。
下载地址:https://github.com/ChinaZeng/CustomView

参考链接:http://www.jianshu.com/p/6e4b3eebbba0

-------------The End-------------